﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;

namespace Look_Through_Windows.Network
{
    public class NetServer
    {
        private TcpListener listener;
        public readonly HashSet<NetConnection> connections = new HashSet<NetConnection>( );

        public event ConnectionDelegate ConnectionAccept;
        public event ConnectionDelegate ConnectionClose;
        public event ConnectionDelegate ConnectionDisconnect;
        public event ConnectionStateDelegate ConnectionStateChange;
        public event MessageRecieveDelegate MessageRecieve;

        public int Port
        {
            get
            {
                if ( listener != null ) return ( ( IPEndPoint )listener.LocalEndpoint ).Port;
                return -1;
            }
        }

        public NetServer( int port )
        {
            listener = new TcpListener( IPAddress.Any, port );
            listener.Start( );
            listener.BeginAcceptTcpClient( AsyncAcceptConnection, null );
            //listener.Server.
        }

        public void Close( )
        {
            listener.Stop( );
            lock ( connections )
            {
                foreach ( NetConnection conn in connections )
                {
                    conn.Close( );
                }
            }
            listener = null;
        }

        private void AsyncAcceptConnection( IAsyncResult result )
        {
            if ( listener == null ) return;
            listener.BeginAcceptTcpClient( AsyncAcceptConnection, null );

            if ( result.IsCompleted )
            {
                TcpClient client = listener.EndAcceptTcpClient( result );
                NetConnection conn = new NetConnection( client, System.Threading.ThreadPriority.Lowest );
                lock ( connections )
                {
                    connections.Add( conn );
                }
                conn.ConnectionDisconnect +=
                    c => { if ( ConnectionDisconnect != null ) ConnectionDisconnect( c ); };
                conn.ConnectionClose +=
                    c => { if ( ConnectionClose != null ) ConnectionClose( c ); };
                conn.ConnectionStateChange +=
                    ( c, ns ) => { if ( ConnectionStateChange != null ) ConnectionStateChange( c, ns ); };
                conn.MessageRecieve +=
                    ( msg, c ) => { if ( MessageRecieve != null ) MessageRecieve( msg, c ); };
                if ( ConnectionAccept != null )
                    ConnectionAccept( conn );
            }
        }

        public void CheckMessages( )
        {
            lock ( connections )
                foreach ( NetConnection conn in connections )
                    conn.CheckMessages( );
        }
        public void CheckOneMessage( )
        {
            lock ( connections )
                foreach ( NetConnection conn in connections )
                    conn.CheckOneMessages( );
        }

        public void RemoveConnection( NetConnection conn )
        {
            lock ( connections )
            {
                if ( connections.Contains( conn ) )
                {
                    connections.Remove( conn );
                    //AUTOCALL: ConnClose event
                    conn.Close( );
                }
            }
        }

        public void Broadcast( NetMessage msg )
        {
            lock ( connections )
            {
                foreach ( NetConnection conn in connections )
                    conn.Send( msg );
            }
        }

        public void Broadcast( NetMessage msg, int state )
        {
            lock ( connections )
            {
                foreach ( NetConnection conn in connections )
                    if ( state == conn.State )
                        conn.Send( msg );
            }
        }

        public void SendMessage( NetMessage msg, NetConnection conn )
        {
            conn.Send( msg );
        }

        public void SendMessage( NetMessage msg, IEnumerable<NetConnection> conns )
        {
            foreach ( NetConnection conn in conns )
                conn.Send( msg );
        }

        public void Clear( )
        {
            List<NetConnection> conns = new List<NetConnection>( );
            lock ( connections )
                foreach ( NetConnection conn in connections )
                    conns.Add( conn );
            foreach ( NetConnection conn in conns )
                RemoveConnection( conn );
        }
    }
}